#coding:utf-8
from PyQt4 import QtCore

import inspect

from twisted.words.protocols.jabber import xmlstream
from twisted.words.protocols.jabber import client
from twisted.words.protocols.jabber import jid as _jid

from twisted.internet import reactor

from twisted.words.xish import domish

from .KeepAlive import KeepAlive

from include import Muc,Addins

def addDerived(xpath):
        assert isinstance(xpath,basestring)
        def wrapper(f):
                f._derrived_xpath = xpath
                return f
        return wrapper
        
def addBootstrap(event):
        assert isinstance(event,basestring)
        def wrapper(f):
                f._bootstrap = event
                return f
        return wrapper
        


class Connector(QtCore.QObject,Muc.Muc):
        changeState = QtCore.pyqtSignal(int)
        openStream = QtCore.pyqtSignal(bool)
        closeStream = QtCore.pyqtSignal()
        rawStream = QtCore.pyqtSignal("PyQt_PyObject")
        
        states = Addins.enum("connected","disconnected","connecting","connErr","streamError","authErr","authOk")
        def __init__(self,jid,password,port = 5222,parent=None):
                super(Connector, self).__init__(parent)
                self.state = Connector.states.disconnected
                self.errorStream = None
                self.password = password
                self.port = port
                self.selfJid = _jid.JID(jid)
                self.__connection = None
                self.mainStream = None
                self.iqWaiting =  {}
                self.features  =  []
                self.xping = KeepAlive(self)
                self.clientFactory=client.XMPPClientFactory(self.selfJid,password) 
                for __name__,handle in inspect.getmembers(self,callable):
                        if hasattr(handle,"_bootstrap"):
                                self.clientFactory.addBootstrap(handle._bootstrap,handle)
        @addBootstrap("//event/stream/authd")
        def _authSucess(self,xmlstream):
                self.mainStream = xmlstream
                xmlstream.addObserver("/*",lambda xml : self.rawStream.emit(xml))
                for ext in self.children()+[self]:
                       for __name__,handle in inspect.getmembers(ext,callable):
                               if hasattr(handle,"_derrived_xpath"):
                                       xmlstream.addObserver(handle._derrived_xpath,handle)
                self.xping.start()
                self.openStream.emit(True)
                self.state = Connector.states.authOk
                self.changeState.emit(Connector.states.authOk)
        @addBootstrap("//event/xmpp/initfailed")
        def _authFail(self,xmlstream):
                self.openStream.emit(False)
                self.state  = Connector.states.authErr
                self.changeState.emit(Connector.states.authErr)
        @addBootstrap("//event/stream/connected")
        def _onConnected(self,xmlstream):
                self.changeState.emit(Connector.states.connected)
        @addBootstrap("//event/stream/end")
        def _endStream(self,xmlstream):
                self.changeState.emit(Connector.states.disconnected)
        @addBootstrap("//event/stream/error")
        def _errorStream(self,xmlstream):
                self.changeState.emit(Connector.states.streamError)
        def addExtension(self,cls):
                assert isinstance(cls,QtCore.QObject)
                cls.setParent(self)
        @QtCore.pyqtSlot()
        def connect(self):
                if self.__connection:
                        self.__connection.connect()
                else:
                        self.__connection=reactor.connectTCP(self.selfJid.host,self.port,self.clientFactory)
                self.changeState.emit(Connector.states.connecting)
        @QtCore.pyqtSlot()
        def disconnect(self):
                if self.clientFactory:
                        self.clientFactory.stopTrying()
                if self.disconnect:
                        self.__connection.disconnect()
                self.mainStream = None
        @QtCore.pyqtSlot("QString")
        @QtCore.pyqtSlot("PyQt_PyObject")
        def send(self,xml):
                self.mainStream.send(xml)
        @addDerived("/iq[@type='get'][@id]/ping[@xmlns='urn:xmpp:ping']")
        def pingReply(self,element):
                iqResponse = domish.Element((None,"iq"))
                iqResponse["to"] = element["from"]
                iqResponse["id"] = element["id"]
                iqResponse["type"] = "result"
                self.send(iqResponse)
        @addDerived("/iq")
        def IdDispatch(self,element):
                if not element.hasAttribute("from"):
                        return
                iqID = element.getAttribute("id")
                if not iqID is None:
                        if iqID in self.iqWaiting and element.getAttribute("type") in ("result","error"):
                                self.iqWaiting[iqID].__call__(element)
                                return
                        iqResponse = domish.Element((None,"iq"))
                        iqResponse["to"] = element["from"]
                        iqResponse["id"] = iqID
                        iqResponse["type"]  = "error"
                        iqResponse["from"] = self.selfJid.full()
                        err = iqResponse.addElement("error")
                        err["code"] = "501"
                        err["type"] = "cancel"
                        err.addElement("feature-not-implemented")
                        self.send(iqResponse)
                else:
                        element["type"]  = "error"
                        element["to"] = element["from"]
                        element["from"] = self.selfJid.full()
                        err = element.addElement("error")
                        err["code"] = "400"
                        err["type"] = "modify"
                        err.addElement("bad-request")
                        self.send(element)
        
                


